home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK1.toast / Development Kits (Disc 1) / QuickDraw GX / Programming Stuff / Sample Code / Printing Samples / Extensions… / Backwash ƒ / Backwash.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-06-15  |  34.0 KB  |  1,153 lines  |  [TEXT/MPS ]

  1. /*________________________________________________________
  2.  
  3.     File: Backwash.c
  4.     
  5.     C source for a printing extension that adds a
  6.     QuickDraw picture to each page despooled.
  7.  
  8.     Dave Hersey
  9.     Apple Developer Technical Support
  10.  
  11.     11/11/92 - dmh - Created.
  12.      1/28/93 - dmh - Cleaned up for a5 seed CD.
  13.      3/26/93 - dmh - Updated for b1c2 (Translator changes).
  14.      8/24/93 - dmh - Updated for b2.
  15.               - The picture is now rolled into the spool
  16.                file as a resource, and applied to each
  17.                page during despooling.
  18.              - Added a TextEdit compatibility mode which
  19.                strips out the white rectangles drawn by
  20.                apps which use TextEdit to print (like
  21.                TeachText).
  22.              - The "intensity" panel item actually
  23.                functions now.
  24.              - Switched to Exception.h assertion stuff
  25.                for error checking.
  26.      9/09/93 - dmh - AddBackwash now displays progress info.
  27.     12/18/93 - dmh - Updated for b3.
  28.      3/22/94 - dmh - Updated for b4.
  29.      5/03/94 - dmh - Updated for f2.
  30.               - BWDespoolPage can be passed a nil format/shape.
  31.               - The flattened shape resource is no longer purgeable.
  32.                It wasn't making it back from GXDespoolResource
  33.                without getting purged.
  34.      6/14/96 - cn  - Updated to support Universal Interfaces 2.1.
  35.  
  36.     (Note: all functions are in the Mark menu.)
  37.  
  38. __________________________________________________________*/
  39.  
  40. #include "Backwash.h"
  41.  
  42.  
  43. /*******************************************************************
  44.     __Startup__ contains our jump table to the overrides.
  45.     
  46. ********************************************************************/
  47. #if defined(__MWERKS__)
  48. asm void __Startup__(void)
  49. {
  50.     DC.L        0                // GX needs this
  51.  
  52.     JMP        BWInitialize        // (offset =  4)
  53.     JMP        BWShutDown            // (offset =  8)
  54.     JMP        BWJobPrintDialog    // (offset = 12)
  55.     JMP        BWHandlePanelEvent    // (offset = 16)
  56.     JMP        BWCreateSpoolFile    // (offset = 20)
  57.     JMP        BWDespoolPage        // (offset = 24)
  58.     JMP        BWCloseSpoolFile    // (offset = 28)
  59.  
  60.     RTS                            // this is needed so __Startup__ symbol works
  61. }
  62. #endif
  63.  
  64.  
  65. /*******************************************************************
  66.     MyInitDataHandle is used to initialize any global data we need
  67.     from our GXInitialize message override.  .
  68.  
  69. ********************************************************************/
  70.  
  71. void MyInitDataHandle( MyDataHdl dataHandle )
  72. {
  73.     (**dataHandle).backwashShape = nil;
  74. }
  75.  
  76.  
  77. /*******************************************************************
  78.     GetBackwashShape returns the backwash shape from the structure
  79.     whose handle is stored ing the MessageHandler InstanceContext.
  80.  
  81. ********************************************************************/
  82. gxShape GetBackwashShape ( void )
  83. {
  84.     MyDataHdl dataHandle;
  85.     dataHandle = (MyDataHdl) GetMessageHandlerInstanceContext();
  86.     return (**dataHandle).backwashShape;
  87. }
  88.  
  89.  
  90. /*******************************************************************
  91.     BWInitialize is our override for the GXInitialize message.
  92.     In here, you shouldn't initialize anything directly-- call
  93.     InitGlobalData for that.  Once you create the A5 world with
  94.     NewMessageGlobals, you can access your global data just like
  95.     you were an app.  Whenever you're called, your global data will
  96.     be valid.
  97.     
  98. ********************************************************************/
  99.  
  100. OSErr BWInitialize()
  101. {
  102.     OSErr        err;
  103.     MyDataHdl    dataHandle;
  104.     
  105.     /*
  106.     Create a new temporary memory handle, initialize
  107.     it, and store it as the message handler's instance
  108.     context.
  109.     */
  110.     
  111.     dataHandle = (MyDataHdl) TempNewHandle(sizeof(MyDataRec),&err);
  112.     
  113.     if (err == noErr)
  114.     {
  115.         MyInitDataHandle(dataHandle);
  116.         SetMessageHandlerInstanceContext(dataHandle);
  117.     }
  118.     
  119.     return err;
  120. }
  121.  
  122.  
  123. /*******************************************************************
  124.     BWShutDown is our override for the GXShutDown message.  We
  125.     simply throw away our A5 world.
  126.     
  127. ********************************************************************/
  128.  
  129. OSErr BWShutDown()
  130. {
  131.     MyDataHdl dataHandle;
  132.     
  133.     /*
  134.     Retrieve the message handler's instance context. If the
  135.     value returned isn't nil, it's a handle that we stored
  136.     earlier. Dispose of the handle and set the instance
  137.     context to nil to "clear" it.
  138.     */
  139.     
  140.     dataHandle = (MyDataHdl) GetMessageHandlerInstanceContext();
  141.     
  142.     if (dataHandle != nil)
  143.     {
  144.         DisposeHandle((Handle) dataHandle);
  145.         SetMessageHandlerInstanceContext(nil);
  146.     }
  147.     
  148.     return noErr;
  149. }
  150.  
  151.  
  152. /*******************************************************************
  153.     BWJobPrintDialog is our override for GXJobPrintDialog.  All we
  154.     do is set up our panel and then forward the message.
  155.     
  156. ********************************************************************/
  157.  
  158. OSErr BWJobPrintDialog(gxDialogResult *dlogResult)
  159. {
  160.     OSErr    err;
  161.     
  162.     err = SetUpPrintPanel();
  163.  
  164.     if (!err)
  165.         err = Forward_GXJobPrintDialog(dlogResult);
  166.     
  167.     return err;
  168. }
  169.  
  170.  
  171. /*******************************************************************
  172.     BWHandlePanelEvent is our override for GXHandlePanelEvent.
  173.     If the event is one of ours, we handle it.  Otherwise, we
  174.     just forward it down the chain.
  175.     
  176. ********************************************************************/
  177.  
  178. OSErr BWHandlePanelEvent(gxPanelInfoRecord *panelInfo)
  179. {
  180.     OSErr                        err = noErr;
  181.     GrafPtr                        oldPort;
  182.     DialogPtr                    pDlg;
  183.     StandardFileReply            reply;
  184.     SFTypeList                    typeList;
  185.     BackwashCollection            backwashConfig;
  186.     Handle                        hItem;
  187.     Rect                        itemRect;
  188.     short                        itemType, oldResFile;
  189.  
  190. // Make sure that our resource file is at the top of the chain.
  191.  
  192.     oldResFile = CurResFile();
  193.     UseResFile(GXGetMessageHandlerResFile());
  194.  
  195.  
  196. // Set our current QuickDraw port to the dialog.
  197.  
  198.     pDlg = panelInfo->pDlg;
  199.     GetPort(&oldPort);
  200.     SetPort(pDlg);
  201.     
  202.     switch (panelInfo->panelEvt)
  203.     {
  204.  
  205. // If our panel is opening, go do any initialization we need to.
  206.  
  207.         case gxPanelOpenEvt:
  208.             OpenBackwashPanel(pDlg, panelInfo->itemCount);
  209.             break;
  210.  
  211.  
  212. // If the user hits the "Select Picture" button, prompt them for a file to load.
  213. // If they select one, get our old collection item, move the FSSpec info to it,
  214. // and replace the old collection item with our modified one.
  215.  
  216.         case gxPanelHitEvt:
  217.             if (panelInfo->itemHit == (panelInfo->itemCount + d_SelectPicture))
  218.             {
  219.  
  220. // Have the user select the PICT file to load.
  221.  
  222.                 typeList[0] = 'PICT';
  223.                 StandardGetFile(nil, 1, typeList, &reply);
  224.                 require(reply.sfGood, UserCancelledFileDialog);
  225.  
  226.  
  227. // Get the old "Backwash settings" collection item, and update the file reference.
  228.  
  229.                 err = GetJobCollectionItem(&backwashConfig,
  230.                                            nil,
  231.                                            kBackwashCollectionType,
  232.                                            kBackwashSettingsID);
  233.  
  234.                 nrequire(err, GetSettings_Failed);
  235.                 
  236.                 backwashConfig.haveFileInfo = true;
  237.                 BlockMove(&reply.sfFile, &backwashConfig.fileInfo, sizeof(FSSpec));
  238.  
  239.  
  240. // Replace the old collection item with the updated one.
  241.  
  242.                 err = AddCollectionItem(GXGetJobCollection(GXGetJob()),
  243.                                         kBackwashCollectionType,
  244.                                         kBackwashSettingsID,
  245.                                         sizeof(BackwashCollection),
  246.                                         &backwashConfig);
  247.  
  248.                 nrequire(err, StoreSettings_Failed);
  249.  
  250.  
  251. // Update the file name in our dialog.
  252.  
  253.                 GetDItem(panelInfo->pDlg, panelInfo->itemCount +d_FileNameItem,
  254.                          &itemType, &hItem, &itemRect);
  255.  
  256.                 SetIText(hItem, backwashConfig.fileInfo.name);
  257.             }
  258.             break;
  259.  
  260.  
  261. // If our panel is activating/deactivating or if the focus (which
  262. // section of the dialog is active) has changed we need to activate
  263. // our editText field appropriately.
  264.  
  265.         case gxPanelActivateEvt:
  266.         case gxPanelDeactivateEvt:
  267.         case gxPanelIconFocusEvt:
  268.         case gxPanelPanelFocusEvt:
  269.             if ((((DialogPeek) pDlg)->editField +1) == (panelInfo->itemCount +d_Intensity))
  270.             {
  271.                 if (panelInfo->panelEvt == gxPanelPanelFocusEvt)
  272.                     TEActivate(((DialogPeek) pDlg)->textH);
  273.                 else
  274.                     TEDeactivate(((DialogPeek) pDlg)->textH);
  275.             }
  276.             break;
  277.     }
  278.  
  279. // Jump points for misc. error conditions follow.
  280.  
  281. UserCancelledFileDialog:
  282. GetSettings_Failed:
  283. StoreSettings_Failed:
  284.  
  285.  
  286. // Finally, restore the original QuickDraw GrafPort and return.
  287.  
  288.     SetPort(oldPort);
  289.     UseResFile(oldResFile);
  290.     return err;
  291. }
  292.  
  293.  
  294. /*******************************************************************
  295.     BWCreateSpoolFile is our override for GXCreateSpoolFile.
  296.     It just gets our user settings collection item, sees if we're
  297.     supposed to do anything, and if so, adds a gxShape version of
  298.     the selected PICT to the spool file.
  299.  
  300. ********************************************************************/
  301.  
  302. OSErr BWCreateSpoolFile(FSSpecPtr anFSSpec, long createOptions, gxSpoolFile *theSpoolFile)
  303. {
  304.     OSErr                err;
  305.     BackwashCollection    backwashConfig;
  306.  
  307. // Forward the message so that the spool file is created for us.
  308.  
  309.     err = Forward_GXCreateSpoolFile(anFSSpec, createOptions, theSpoolFile);
  310.     nrequire(err, ForwardMessage_Failed);
  311.  
  312. // Get our collection item and see if we're enabled and have file info.  If
  313. // so, add the backwash picture to the spool file.  Note that if we don't
  314. // find our collection item we don't try to add the backwash.  The user may
  315. // not be printing with dialogs, so you have to deal with this situation…
  316. // Also, if we get an error when we try to add the Backwash shape, we
  317. // ignore the error but turn ourself off by removing our settings collection
  318. // item.  This way, printing can continue, and BWDespoolPage won't be
  319. // confused when there's no Backwash shape in the print file.
  320.  
  321.     err = GetJobCollectionItem(&backwashConfig,
  322.                                nil,
  323.                                kBackwashCollectionType,
  324.                                kBackwashSettingsID);
  325.                            
  326.     if (!err && backwashConfig.addBackwash && backwashConfig.haveFileInfo)
  327.     {
  328.         err = (OSErr) AddBackwash(&backwashConfig.fileInfo, (short) backwashConfig.intensity, *theSpoolFile);
  329.         require(err, BackwashAdded);
  330.         
  331.         RemoveCollectionItem(GXGetJobCollection(GXGetJob()),
  332.                              kBackwashCollectionType,
  333.                              kBackwashSettingsID);
  334.  
  335.         err = noErr;
  336.     }
  337.     else
  338.         if (err == collectionItemNotFoundErr)
  339.             err = noErr;
  340.  
  341. BackwashAdded:
  342. ForwardMessage_Failed:
  343.  
  344.     return err;
  345. }
  346.  
  347.  
  348. /*******************************************************************
  349.     BWDespoolPage is our override for the GXDespoolPage message.
  350.     We check to see if the user is adding a backwash, and if so,
  351.     retrieve the flattened shape from the spool file and apply it
  352.     to each page.  For efficiency, we only retrieve the shape once,
  353.     and then reference it via our instance-global, iBackwashShape.  That
  354.     shape is latered disposed of in BWCloseSpoolFile.
  355.     
  356. ********************************************************************/
  357.  
  358. OSErr BWDespoolPage(gxSpoolFile theSpoolFile, long thePageNum,
  359.                     gxFormat theFormat, gxShape *thePage,
  360.                     Boolean *formatChanged)
  361. {
  362.     OSErr                anOSErr;
  363.     gxGraphicsError        grErr;
  364.     Handle                vpListHdl, gxShapeHdl = nil;
  365.     gxRectangle            pBounds;
  366.     Fixed                cx, cy, px, py;
  367.     long                numViewPorts;
  368.     BackwashCollection    backwashConfig;
  369.     gxShape                iBackwashShape ;
  370.  
  371.     iBackwashShape = GetBackwashShape() ;
  372.     
  373. // Forward the message so that we get the page shape, then retrieve our
  374. // "Backwash settings" collection item.
  375.  
  376.     anOSErr = Forward_GXDespoolPage(theSpoolFile, thePageNum, theFormat, thePage, formatChanged);
  377.     nrequire((grErr = (gxGraphicsError) anOSErr), ForwardMessage_Failed);
  378.     require((theFormat != nil) && (thePage != nil), NotAddingBackwash);
  379.  
  380.     grErr = GetJobCollectionItem(&backwashConfig,
  381.                                  nil,
  382.                                  kBackwashCollectionType,
  383.                                  kBackwashSettingsID);
  384.  
  385.     require_action((!grErr && backwashConfig.addBackwash && backwashConfig.haveFileInfo),
  386.                     NotAddingBackwash, grErr = noErr;);
  387.  
  388.  
  389. // If we're in "TextEdit Compatibility" mode, remove white rectangles from the
  390. // page shape.
  391.  
  392.     if (backwashConfig.useTextEditMode)
  393.         RemoveWhiteRects(*thePage);
  394.  
  395.  
  396. // If we need to load the flattened shape resource, do so.  Note that we only
  397. // need to do this on the first page.  Thereafter, the shape reference is in
  398. // our instance-global.
  399.  
  400.     if (iBackwashShape == nil)
  401.     {
  402.         anOSErr = Send_GXDespoolResource(theSpoolFile, kBackwashCollectionType, r_BackwashPICTID, &gxShapeHdl);
  403.         nrequire((grErr = (gxGraphicsError) anOSErr), DespoolResource_Failed);
  404.     }
  405.  
  406.  
  407. // If we haven't already, unflatten the Backwash shape.  We need a
  408. // viewport list to unflatten the shape, so we get the current page's
  409. // viewport list and use that.
  410.  
  411.     if (iBackwashShape == nil)
  412.     {
  413.  
  414.         numViewPorts = GXGetShapeViewPorts(*thePage, nil);
  415.  
  416.         vpListHdl = TempNewHandle(numViewPorts * sizeof(gxViewPort), &anOSErr);
  417.         grErr = (gxGraphicsError) anOSErr;
  418.         nrequire_action(grErr, TempNewHandle_Failed, ReleaseResource(gxShapeHdl););
  419.     
  420.         HLock(vpListHdl);
  421.         GXGetShapeViewPorts(*thePage, (gxViewPort *) *vpListHdl);
  422.         iBackwashShape = HandleToShape(gxShapeHdl, numViewPorts, (gxViewPort *) *vpListHdl);
  423.         DisposHandle(vpListHdl);            // all done with this.
  424.  
  425.         if (gxShapeHdl)
  426.             ReleaseResource(gxShapeHdl);    // all done with this-- remember, it's a resource!
  427.  
  428.         nrequire(GXGetGraphicsError(&grErr), CouldNotSetUpShape);
  429.     }
  430.  
  431.  
  432. // Now we get the page dimensions from the current page format.  We use this
  433. // and the shape's bounds to center the picture on the page.  Once we've done
  434. // that, we add the GX picture shape behind all the other shapes in our page
  435. // shape and return.
  436.  
  437.     GXGetFormatDimensions(theFormat, &pBounds, nil);
  438.     grErr = GXGetJobError(GXGetJob());
  439.     nrequire(grErr, GetFormatDims_Failed);
  440.  
  441.     cx = pBounds.left + (pBounds.right - pBounds.left) >>1;
  442.     cy = pBounds.top + (pBounds.bottom - pBounds.top) >>1;
  443.  
  444.     GXGetShapeBounds(iBackwashShape, 0, &pBounds);
  445.     px = pBounds.left + (pBounds.right - pBounds.left) >>1;
  446.     py = pBounds.top + (pBounds.bottom - pBounds.top) >>1;
  447.     GXMoveShapeTo(iBackwashShape, cx-px, cy-py);
  448.  
  449.     GXSetPictureParts(*thePage, 1, 0, 1, &iBackwashShape, nil, nil, nil);
  450.     GXGetGraphicsError(&grErr);
  451.  
  452.  
  453. // Jump points for misc. error conditions follow.
  454.  
  455. GetFormatDims_Failed:
  456. CouldNotSetUpShape:
  457. TempNewHandle_Failed:
  458. DespoolResource_Failed:
  459. NotAddingBackwash:
  460. ForwardMessage_Failed:
  461.  
  462.     return grErr;
  463. }
  464.  
  465.  
  466. /*******************************************************************
  467.     BWCloseSpoolFile is our override for the GXCloseSpoolFile
  468.     message.  If the instance-global shape "iBackwashShape" is not nil, we
  469.     dispose of it.  Remember that we initialized it to nil in
  470.     BWInitialize, so the only way it can be non-nil is if we just
  471.     used it during despooling.
  472.     
  473. ********************************************************************/
  474.  
  475. OSErr BWCloseSpoolFile(gxSpoolFile theSpoolFile, long closeOptions)
  476. {
  477.     gxShape                iBackwashShape ;
  478.  
  479.     iBackwashShape = GetBackwashShape() ;
  480.  
  481. // Dispose of the shape and "nil out" our global, if necessary.
  482. // Then, forward the message so that the spool file is closed.
  483.  
  484.     if (iBackwashShape != nil)
  485.     {
  486.         GXDisposeShape(iBackwashShape);
  487.         iBackwashShape = nil;
  488.     }
  489.  
  490.     return Forward_GXCloseSpoolFile(theSpoolFile, closeOptions);
  491. }
  492.  
  493.  
  494. /*******************************************************************
  495.     SetUpPrintPanel sets up our print panel, adding a default
  496.     "Backwash settings" collection item to the job collection if
  497.     there isn't already one.  This collection item has the values
  498.     we'll use to set up our panel's controls.
  499.     
  500. ********************************************************************/
  501.  
  502. OSErr SetUpPrintPanel()
  503. {
  504.     OSErr                        err;
  505.     gxPanelSetupRecord            panelSetupRec;
  506.     BackwashCollection            backwashConfig;
  507.  
  508. // Get the job collection and try to find our backwash settings.
  509.  
  510.     err = GetJobCollectionItem(&backwashConfig,
  511.                                nil,
  512.                                kBackwashCollectionType,
  513.                                kBackwashSettingsID);
  514.  
  515.  
  516. // If our collection item (settings) don't exist yet, create a default
  517. // collection item and add that to the job collection.
  518.  
  519.  
  520.     if (err == collectionItemNotFoundErr)
  521.     {
  522.         FSSpec    dafaultFSSpec;
  523.  
  524.         
  525.         dafaultFSSpec.vRefNum = 0;
  526.         dafaultFSSpec.parID = 0;
  527.         BlockMoveData ("\pNone", dafaultFSSpec.name, 5);
  528.  
  529.         backwashConfig.intensity = kDefaultIntensity;
  530.         backwashConfig.addBackwash = kDontAddBackwash;
  531.         backwashConfig.useTextEditMode = false;
  532.         backwashConfig.haveFileInfo = false;
  533.         backwashConfig.fileInfo = dafaultFSSpec ;
  534.  
  535.         err = AddCollectionItem(GXGetJobCollection(GXGetJob()),
  536.                                 kBackwashCollectionType,
  537.                                 kBackwashSettingsID,
  538.                                 sizeof(BackwashCollection),
  539.                                 &backwashConfig);
  540.     }
  541.  
  542.     nrequire(err, StoreSettings_Failed);
  543.  
  544.  
  545. // Now do the actual panel set up.
  546.  
  547.     panelSetupRec.panelResId        = r_BackwashPanel;    // Which panel resource?
  548.     panelSetupRec.resourceRefNum    = GXGetMessageHandlerResFile();    // Where is it?
  549.     panelSetupRec.refCon            = 0;                // We don't use this.
  550.     panelSetupRec.panelKind            = gxExtensionPanel;    // This is an extension panel.
  551.     
  552.     err = GXSetupDialogPanel(&panelSetupRec);
  553.  
  554.  
  555. StoreSettings_Failed:
  556.  
  557.     return err;
  558. }
  559.  
  560.  
  561. /*******************************************************************
  562.     OpenBackwashPanel handles non-'xdtl' item initialization when
  563.     we open our panel.  Note that our items will be offset from
  564.     itemCount.  (So, if we want item #5 in our DITL, we pass
  565.     itemCount +5 to GetDItem.)
  566.     
  567.     The only non-'xdtl' item we have in our panel is the PICTfile
  568.     name.  We initialize that here.
  569.     
  570. ********************************************************************/
  571.  
  572. void OpenBackwashPanel(DialogPtr pDlg, short itemCount)
  573. {
  574.     BackwashCollection    backwashConfig;
  575.     Handle                hItem;
  576.     Rect                itemRect;
  577.     short                itemType;
  578.  
  579. // Initialize the current file name displayed, based on the
  580. // settings in our backwash collection item.
  581.  
  582.     GetJobCollectionItem(&backwashConfig,
  583.                          nil,
  584.                          kBackwashCollectionType,
  585.                          kBackwashSettingsID);
  586.  
  587.     GetDItem(pDlg, itemCount +d_FileNameItem, &itemType, &hItem, &itemRect);
  588.     SetIText(hItem, backwashConfig.fileInfo.name);
  589. }
  590.  
  591.  
  592. /*******************************************************************
  593.     AddBackwash is the routine we call at GXCreateSpoolFile time
  594.     to actually add the backwash to our spool file.  We pass in
  595.     info on where the PICT file is, the intensity the user
  596.     requested (as an integral percentage 0-100), and the spool
  597.     file reference.
  598.  
  599. ********************************************************************/
  600.  
  601. gxGraphicsError AddBackwash(FSSpecPtr fileInfo, short theIntensity, gxSpoolFile theSpoolFile)
  602. {
  603.     gxGraphicsError    grErr;
  604.     Rect            pictBounds, itemRect;
  605.     PicHandle        backwashPict;
  606.     Point            patStretchPoint;            // this means "don't stretch."
  607.     Handle            textHdl, gxShapeHdl = nil;
  608.     gxShape            gxPictShape;
  609.     short            resAttribs, oldResFile, ourResFile, itemKind;
  610.     Str255            statMsg;
  611.     DialogPtr        theMessageDlog;
  612.     unsigned long    endTicks;
  613.     GrafPtr            oldPort;
  614.  
  615.  
  616.     patStretchPoint.h = 1;
  617.     patStretchPoint.v = 1;
  618.  
  619.     GetPort(&oldPort);
  620.     oldResFile = CurResFile();
  621.     ourResFile = GXGetMessageHandlerResFile();
  622.     UseResFile(ourResFile);
  623.  
  624.  
  625. // Load the dialog that we'll display status messages in.  Note that we
  626. // CANNOT use GXAlertTheUser or GXReportStatus in this routine, because
  627. // those only work when the Finder or PrinterShare GX are the active
  628. // applications (when we're spooling, the user's app is active.)  Load
  629. // our first status string, and show/update the dialog.
  630.  
  631.     theMessageDlog = GetNewDialog(r_BackwashStatus, nil, (WindowPtr) -1);
  632.     nrequire((grErr = (gxGraphicsError) ResError()), CouldNotLoadDialog);
  633.     GetDItem(theMessageDlog, d_StatusFieldItem, &itemKind, &textHdl, &itemRect);
  634.  
  635.     GetIndString(statMsg, r_BackwashStatus, s_StatusLoadPict);
  636.     nrequire((grErr = (gxGraphicsError) ResError()), CouldNotLoadString);
  637.     SetIText(textHdl, statMsg);
  638.     SetPort(theMessageDlog);
  639.     PositionWindow(theMessageDlog, true, n_dlogLevel);
  640.     DrawDialog(theMessageDlog);
  641.  
  642.  
  643. // Load the picture from disk.  If we can't load it, we display a message
  644. // saying so, and then exit the routine after 3 seconds.
  645.  
  646.     GXJobIdle();
  647.     backwashPict = LoadAPict(fileInfo);
  648.  
  649.     UseResFile(ourResFile);
  650.     nrequire(backwashPict, PictureLoaded);
  651.     GetIndString(statMsg, r_BackwashStatus, s_PictureNotLoaded);
  652.     nrequire((grErr = (gxGraphicsError) ResError()), CouldNotLoadString);
  653.     SetIText(textHdl, statMsg);
  654.     
  655.     SysBeep(0);
  656.     endTicks = TickCount() + (3 * 60);
  657.     
  658.     while (endTicks > TickCount())
  659.         GXJobIdle();
  660.  
  661.     require_action(backwashPict, CouldNotLoadPICT, grErr = nilHandleErr;);
  662.  
  663.  
  664. // Create a shape to store our converted PICT in, load and display another
  665. // status string, and then convert the PICT into a QuickDraw GX shape.
  666.  
  667. PictureLoaded:
  668.  
  669.     pictBounds = (*backwashPict)->picFrame;
  670.     gxPictShape = GXNewShape(gxPictureType);            // shape to store picture in.
  671.     nrequire_action(GXGetGraphicsError(&grErr), CouldNotCreateShape,
  672.                     KillPicture(backwashPict););
  673.     
  674.     UseResFile(ourResFile);
  675.     GetIndString(statMsg, r_BackwashStatus, s_ConvertPict);
  676.     nrequire((grErr = (gxGraphicsError) ResError()), CouldNotLoadString);
  677.     SetIText(textHdl, statMsg);
  678.     
  679.     GXJobIdle();
  680.  
  681.     GXConvertPICTToShape(backwashPict,                    /* this is the original.    */
  682.                          gxDefaultOptionsTranslation,    /* use default settings.    */
  683.                          &pictBounds,                    /* source/dest bounds.        */
  684.                          &pictBounds,
  685.                          patStretchPoint,                /* how to stretch (don't).    */
  686.                          gxPictShape,                    /* the destination.            */
  687.                          nil);                            /* we don't want details.    */
  688.     
  689.     KillPicture(backwashPict);                    // original is converted so kill it.
  690.  
  691.     nrequire_action(GXGetGraphicsError(&grErr), CouldNotConvertShape,
  692.                     GXDisposeShape(gxPictShape););
  693.  
  694.  
  695. // Load and display another status string, lighten the GX picture shape as we
  696. // need to, and flatten the shape into a handle.
  697.  
  698.     UseResFile(ourResFile);
  699.     GetIndString(statMsg, r_BackwashStatus, s_SetShapeIntensity);
  700.     nrequire((grErr = (gxGraphicsError) ResError()), CouldNotLoadString);
  701.     SetIText(textHdl, statMsg);
  702.     
  703.     GXJobIdle();
  704.  
  705.     UseResFile(ourResFile);
  706.     grErr = SetShapeIntensity(gxPictShape, theIntensity);
  707.     nrequire_action(grErr, CouldNotSetShapeIntensity, GXDisposeShape(gxPictShape););
  708.     gxShapeHdl = ShapeToHandle(gxPictShape);
  709.     grErr = (gxGraphicsError) MemError();
  710.     GXDisposeShape(gxPictShape);                // all done with this.
  711.     nrequire(grErr, CouldNotFlattenShape);
  712.  
  713.  
  714. // Load and display our last status string, and add the shape as a resource in
  715. // the spool file.  After adding it, update the resource so it's marked
  716. // "sysHeap."  We do this so that the resource won't be loaded into
  717. // PrinterShare GX's heap at despool time.  Since the data may be large, and
  718. // PrinterShare GX's heap is small, this will make things work better.
  719.  
  720.     GetIndString(statMsg, r_BackwashStatus, s_SpoolShapeResource);
  721.     nrequire((grErr = (gxGraphicsError) ResError()), CouldNotLoadString);
  722.     SetIText(textHdl, statMsg);
  723.     
  724.     GXJobIdle();
  725.  
  726.     grErr = Send_GXSpoolResource(theSpoolFile, gxShapeHdl, kBackwashCollectionType, r_BackwashPICTID);
  727.     nrequire_action(grErr, CouldNotAddShape, DisposHandle(gxShapeHdl););
  728.  
  729.     resAttribs = GetResAttrs(gxShapeHdl);
  730.     SetResAttrs(gxShapeHdl, resAttribs | resSysHeap);
  731.     ChangedResource(gxShapeHdl);
  732.     WriteResource(gxShapeHdl);
  733.  
  734.  
  735. // Finally release the resource (DON'T call DisposHandle on it!!), dispose of
  736. // our dialog, and return.
  737.  
  738.     ReleaseResource(gxShapeHdl);
  739.  
  740.  
  741. // Jump points for misc. error conditions follow.
  742.  
  743. CouldNotAddShape:
  744. CouldNotFlattenShape:
  745. CouldNotSetShapeIntensity:
  746. CouldNotConvertShape:
  747. CouldNotCreateShape:
  748. CouldNotLoadPICT:
  749. CouldNotLoadString:
  750.  
  751.     SetPort(oldPort);
  752.     DisposDialog(theMessageDlog);
  753.  
  754. CouldNotLoadDialog:
  755.  
  756.     UseResFile(oldResFile);
  757.     return grErr;
  758. }
  759.  
  760.  
  761. /*******************************************************************
  762.     RemoveWhiteRects is the routine which strips white rectangles
  763.     out of a page shape if we're in "TextEdit compatibility" mode.
  764.     Apps which print text by using TextEdit and calling TEUpdate
  765.     inadvertently add white rectangles behind the text they're
  766.     drawing.  This is because TEUpdate erases before it draws, and
  767.     this erasure is translated into a white rectangle by GX.  It's
  768.     as if you drew a white rectangle shape (to erase) and then
  769.     drew the text on top of that.  This is yet another reason why
  770.     DTS recommends that you not use TextEdit for printing.
  771.     
  772.     This routine calls itself recursively if a picture is passed.
  773.     The return value for each call indicates whether or not you
  774.     should remove the shape passed.  The original caller is
  775.     expected to pass a picture in, so it need not worry about the
  776.     boolean value returned.  (It should never be set, since a
  777.     picture ≠ a white rect.)
  778.  
  779. ********************************************************************/
  780.  
  781. Boolean RemoveWhiteRects(gxShape theShape)
  782. {
  783.     gxGraphicsError        grErr = noErr;
  784.     gxShapeType            typeOfShape;
  785.     gxShape                nextShape;
  786.  
  787. // Get the type of shape we were passed.  If it's a picture shape,
  788. // get the number of parts in the picture.
  789.  
  790.     require(theShape, ShapeIsNIL);
  791.     typeOfShape = GXGetShapeType(theShape);
  792.  
  793.     if (typeOfShape == gxPictureType)
  794.     {
  795.         long    idx, numParts;
  796.         
  797.         numParts = GXGetPicture(theShape, nil, nil, nil, nil);
  798.  
  799.  
  800. // Loop through each picture item.  If RemoveWhiteRects says it's a
  801. // white rectangle, remove it.  When we remove a picture item, we
  802. // must decrement both the picture item index and the number of
  803. // items in the picture.  That way, our loop still works.
  804.  
  805.         for (idx = 1; idx <= numParts; idx ++)
  806.         {
  807.             GXGetPictureParts(theShape, idx, 1, &nextShape, nil, nil, nil);
  808.  
  809.             if (RemoveWhiteRects(nextShape))
  810.             {
  811.                 GXSetPictureParts(theShape, idx, 1, 0, nil, nil, nil, nil);
  812.                 idx -= 1;
  813.                 numParts -= 1;
  814.             }
  815.         }
  816.     }
  817.     else
  818.  
  819. // We weren't passed a picture.  See if it's a white rectangle which is being
  820. // "source copied."  If so, return true-- this may be a TextEdit erasure.
  821.  
  822.         if (typeOfShape == gxRectangleType)
  823.         {
  824.             gxColor            shapeColor;
  825.             gxTransferMode    shapeTransfer;
  826.                     
  827.             GXGetShapeColor(theShape, &shapeColor);
  828.             GXGetShapeTransfer(theShape, &shapeTransfer);
  829.  
  830.             if ((shapeColor.element.rgb.red   == 0xFFFF) &&
  831.                 (shapeColor.element.rgb.green == 0xFFFF) &&
  832.                 (shapeColor.element.rgb.blue  == 0xFFFF) &&
  833.                 (shapeTransfer.component[0].mode == gxCopyMode))
  834.                 return true;
  835.         }
  836.  
  837. ShapeIsNIL:
  838.  
  839.     return false;
  840. }
  841.  
  842.  
  843. /*******************************************************************
  844.     SetShapeIntensity is a routine which lightens the shape passed
  845.     based on the percentage specified.  For example, an intensity
  846.     of 100 (100%) means that the shape should appear normal.  At
  847.     50, (50%) the shape will be half as dark, at 0 (0%) the shape
  848.     would simply fade to white.
  849.     
  850.     This routine calls itself recursively if a picture is passed.
  851.     For each item in the shape passed, (and all items contained in
  852.     sub-pictures), the shape's ink is set to blend mode, and the
  853.     item is blended by the percentage passed.  Since the background
  854.     during printing is white, this effectively fades a shape to
  855.     white.
  856.  
  857. ********************************************************************/
  858.  
  859. gxGraphicsError SetShapeIntensity(gxShape theShape, short theIntensity)
  860. {
  861.     gxGraphicsError        grErr = noErr;
  862.     gxShapeType            typeOfShape;
  863.     gxShape                nextShape;
  864.  
  865. // Get the type of shape we were passed.  If it's a picture shape,
  866. // get the number of parts in the picture.
  867.  
  868.     require(theShape, ShapeIsNIL);
  869.     require((theIntensity != 100), UsingFullIntensity);
  870.     typeOfShape = GXGetShapeType(theShape);
  871.  
  872.     if (typeOfShape == gxPictureType)
  873.     {
  874.         long    idx, numParts;
  875.         
  876.         numParts = GXGetPicture(theShape, nil, nil, nil, nil);
  877.  
  878.  
  879. // For each item in the picture, set the shape's intensity.
  880.  
  881.         for (idx = 1; !grErr && (idx <= numParts); idx ++)
  882.         {
  883.             GXGetPictureParts(theShape, idx, 1, &nextShape, nil, nil, nil);
  884.             grErr = SetShapeIntensity(nextShape, theIntensity);
  885.         }
  886.     }
  887.     else
  888.  
  889. // We weren't passed a picture.  Get the ink of this shape and set its
  890. // first (and only) transfer mode to "blend", maximize and minimize all
  891. // the bounding values, and set the transfer's operand to the percentage
  892. // passed in (scaled so that it's between 0 and 0xFFFF).  Finally, set
  893. // the shape's transfer to this new value.
  894.  
  895.     {
  896.         gxTransferMode    shapeTransfer;
  897.         
  898.         GXGetShapeTransfer(theShape, &shapeTransfer);
  899.  
  900.         shapeTransfer.flags = gxSingleComponentTransfer;
  901.  
  902.         shapeTransfer.component[0].mode = gxBlendMode;
  903.         shapeTransfer.component[1].mode =
  904.         shapeTransfer.component[2].mode =
  905.         shapeTransfer.component[3].mode = 0;
  906.  
  907.         shapeTransfer.component[0].flags = 0;
  908.         shapeTransfer.component[0].sourceMinimum = 0;
  909.         shapeTransfer.component[0].sourceMaximum = 0xFFFF;
  910.         shapeTransfer.component[0].deviceMinimum = 0;
  911.         shapeTransfer.component[0].deviceMaximum = 0xFFFF;
  912.         shapeTransfer.component[0].clampMinimum = 0;
  913.         shapeTransfer.component[0].clampMaximum = 0xFFFF;
  914.         shapeTransfer.component[0].operand = (0xFFFF * theIntensity)/100;
  915.  
  916.         GXSetShapeTransfer(theShape, &shapeTransfer);
  917.         GXGetGraphicsError(&grErr);
  918.     }
  919.  
  920. ShapeIsNIL:
  921. UsingFullIntensity:
  922.  
  923.     return grErr;
  924. }
  925.  
  926.  
  927. /*******************************************************************
  928.     LoadAPict is a utility routine that just loads a PICT.  We
  929.     create a MultiFinder temporary memory handle and return the
  930.     pict in that.  This way, we don't impose on the application's
  931.     memory space.
  932.  
  933. ********************************************************************/
  934.  
  935. PicHandle LoadAPict(FSSpecPtr opFSSpec)
  936. {
  937.     OSErr            err;
  938.     long            count;
  939.     short            pictRefNum;
  940.     PicHandle        backwashPict = nil;
  941.  
  942. // Open the file.
  943.  
  944.     err = FSpOpenDF(opFSSpec, fsCurPerm, &pictRefNum);
  945.     nrequire(err, CouldNotOpenFile);
  946.  
  947. // Get the length of the file minus the 512 byte picture header.
  948.  
  949.     err = GetEOF(pictRefNum, &count);
  950.     nrequire(err, CouldNotGetEOF);
  951.     count -= 512;
  952.  
  953.  
  954. // Create a handle for our PICT and read the data (Except the header)
  955. // from the file into the handle.
  956.  
  957.     err = SetFPos(pictRefNum, fsFromStart, 512);
  958.     nrequire(err, CouldNotSetFilePos);
  959.  
  960.     backwashPict = (PicHandle) TempNewHandle(count, &err);
  961.     nrequire(err, CouldNotCreateHandle);
  962.  
  963.     HLock((Handle) backwashPict);
  964.     err = FSRead(pictRefNum, &count, *backwashPict);
  965.     HUnlock((Handle) backwashPict);
  966.  
  967.  
  968. // If we had an error, kill the picture.  Otherwise, just close the file.
  969.  
  970.     if(err)
  971.     {
  972.         KillPicture(backwashPict);
  973.         backwashPict = nil;
  974.     }
  975.  
  976. CouldNotCreateHandle:
  977. CouldNotSetFilePos:
  978. CouldNotGetEOF:
  979.  
  980.     FSClose(pictRefNum);
  981.  
  982. CouldNotOpenFile:
  983.  
  984.     return backwashPict;
  985. }
  986.  
  987.  
  988. /*******************************************************************
  989.     GetJobCollectionItem is a generic routine that retrieves a
  990.     collection item from the job collection.
  991.     
  992. ********************************************************************/
  993.  
  994. OSErr GetJobCollectionItem(void *collectItem, long *collectSize,
  995.                            OSType collectType, short collectID)
  996. {
  997.     return GetCollectionItem(GXGetJobCollection(GXGetJob()),
  998.                              collectType,
  999.                              collectID,
  1000.                              collectSize,
  1001.                              collectItem);
  1002. }
  1003.  
  1004.  
  1005. /*******************************************************************
  1006.     HandleSpoolProc is a spooling routine for GXFlattenShape and
  1007.     GXUnflattenShape (called from ShapeToHandle and HandleToShape).
  1008.     This routine (which was taken from "storage library.c"),
  1009.     flattens a gxShape into a handle.
  1010.  
  1011. ********************************************************************/
  1012.  
  1013. gxGraphicsError HandleSpoolProc(gxSpoolCommand command,  UserSpool *block)
  1014. {
  1015.     OSErr    err = noErr;
  1016.  
  1017.     switch (command)
  1018.     {
  1019.         case gxOpenReadSpool:    // About to unflatten-- reset the handle size and offset.
  1020.  
  1021.             block->size = 0;
  1022.             block->position = 0;
  1023.             break;
  1024.  
  1025.         case gxOpenWriteSpool:    // About to flatten-- create a handle to flatten into,
  1026.                                 // using our initial handle size.
  1027.         
  1028.             block->data = TempNewHandle(kAllocationIncrement, &err);
  1029.             block->size = kAllocationIncrement;
  1030.             block->position = 0;
  1031.             break;
  1032.       
  1033.         case gxReadSpool:        // Some data has been unflattened.  Move the data to
  1034.                                 // our buffer and increase the buffer offset.
  1035.  
  1036.             BlockMove((*(char **) block->data + block->position), block->spool.buffer, block->spool.count);
  1037.             block->position += block->spool.count;
  1038.             break;
  1039.         
  1040.         case gxWriteSpool:        // Some data has been flattened.
  1041.  
  1042.             {
  1043.                 register long oldPosition;
  1044.                 
  1045.                                 // If we need to expand the buffer to hold the
  1046.                                 // new data, do so.  We always make sure there
  1047.                                 // is enough room for one "bufferSize" past the
  1048.                                 // current point.  Move the data to our buffer
  1049.                                 // and increase the buffer offset.
  1050.                 
  1051.                 oldPosition = block->position;
  1052.                 block->position += block->spool.count;
  1053.                 
  1054.                 if (block->position + block->spool.bufferSize > block->size)      
  1055.                 {
  1056.                     block->size += block->spool.bufferSize;
  1057.                     HUnlock(block->data);
  1058.                     SetHandleSize(block->data, block->size);
  1059.                     err = MemError();
  1060.                     HLock(block->data);
  1061.                 }
  1062.                 BlockMove(block->spool.buffer, (*(char **) block->data + oldPosition), block->spool.count);
  1063.             }
  1064.             break;
  1065.       
  1066.         case gxCloseSpool:        // Finishing up.  Size the buffer to the data's
  1067.                                 // final size.
  1068.  
  1069.             SetHandleSize(block->data, block->position);
  1070.             err = MemError();
  1071.             break;
  1072.     }
  1073.  
  1074.     return (gxGraphicsError) err;
  1075. }
  1076.  
  1077.  
  1078. /*******************************************************************
  1079.     ShapeToHandle flattens the passed shape into a handle, which
  1080.     it returns.  This routine was taken from the "storage library.c"
  1081.     file.  It simply calls GXFlattenShape with a custom
  1082.     gxSpoolProcedure which flattens the shape into a handle.
  1083.  
  1084. ********************************************************************/
  1085.  
  1086. Handle ShapeToHandle(gxShape source)
  1087. {
  1088.     UserSpool block;
  1089.     
  1090.     block.spool.spoolProcedure = (long (*)(gxSpoolCommand, struct gxSpoolBlock *)) HandleSpoolProc;
  1091.     block.spool.buffer = nil;
  1092.     block.spool.bufferSize = 0;
  1093.     GXFlattenShape(source, gxFontListFlatten | gxFontGlyphsFlatten, &block.spool);
  1094.     return block.data;
  1095. }
  1096.  
  1097.  
  1098. /*******************************************************************
  1099.     HandleToShape unflattens the passed handle into its original
  1100.     gxShape, which it returns.  This routine was taken from the
  1101.     "storage library.c" file.  It simply calls GXUnflattenShape
  1102.     with a custom gxSpoolProcedure which unflattens the shape from
  1103.     a handle.
  1104.  
  1105. ********************************************************************/
  1106.  
  1107. gxShape HandleToShape(Handle source, long count, const gxViewPort portList[])
  1108. {
  1109.     UserSpool block;
  1110.     
  1111.     block.spool.spoolProcedure = (long (*)(gxSpoolCommand, struct gxSpoolBlock *)) HandleSpoolProc;
  1112.     block.spool.buffer = nil;
  1113.     block.spool.bufferSize = 0;
  1114.     block.data = source;
  1115.     return GXUnflattenShape(&block.spool, count, portList);
  1116. }
  1117.  
  1118.  
  1119. /*******************************************************************
  1120.     PositionWindow - This routine positions a window on the
  1121.     screen, centered horizontally and positioned vertPercent
  1122.     down the current screen.  If showIt is true, we make the
  1123.     window visible, otherwise we hide it.
  1124.  
  1125. ********************************************************************/
  1126.  
  1127. void PositionWindow(WindowPtr windPtr, Boolean showIt, float vertPercent)
  1128. {
  1129.     Rect        devRect;
  1130.     short        amtFromTop, windWidth, deviceWidth;
  1131.     GDHandle    curGDH;
  1132.  
  1133. // Get the current GDevice and use its bounds to calculate where we
  1134. // should move the window to.
  1135.  
  1136.     if (windPtr != nil)
  1137.     {
  1138.         curGDH = GetGDevice();
  1139.         devRect = (*curGDH)->gdRect;
  1140.  
  1141.         amtFromTop = (devRect.bottom - devRect.top +1) * vertPercent;
  1142.         windWidth = windPtr->portRect.right - windPtr->portRect.left +1;
  1143.         deviceWidth = devRect.right - devRect.left +1;
  1144.  
  1145.  
  1146. // Hide the window, move it, and (if we're supposed to) show it again.
  1147.  
  1148.         ShowHide(windPtr, false);
  1149.         MoveWindow(windPtr, devRect.left + (deviceWidth /2 - windWidth /2), devRect.top + amtFromTop, true);
  1150.         if (showIt) ShowHide(windPtr, true);
  1151.     }
  1152. }
  1153.